"""
Posnet TCP server
"""
import SocketServer, socket, time               # get socket server, handler objects
#from BitPacket import BitStructure,BitField,BYTE_SIZE,INTEGER_SIZE,BitVariableStructure
import array,sets,logging,sys,os

import posnet_overlay_messages
import scheduler

class posnet_job:
	def __init__(self,method_to_execute):
		self.method_to_execute = method_to_execute
	def execute(self): 
		self.method_to_execute()
	def quit(self): return False

class posnet_tcp_server(SocketServer.TCPServer):
	socket_timeout = 5
	quit_message = "quit"
	server_request_queue_size = 22

	def __init__(self, bind_address,protocol_handler):
		""" """
		SocketServer.TCPServer.allow_reuse_address = 1
		SocketServer.TCPServer.request_queue_size =  posnet_tcp_server.server_request_queue_size
		server = SocketServer.TCPServer.__init__(self,bind_address,None)
		self.__running = False
		self.__bind_address= bind_address
		self.__protocol_handler = protocol_handler
		#-------------
		self.__control_plane_scheduler = scheduler.Scheduler("posnet_control_plane")
		self.__control_plane_scheduler.add_threads(3)
		self.__query_plane_scheduler = scheduler.Scheduler("posnet_query_plane")
		self.__query_plane_scheduler.add_threads(1)
		self.logger = logging.getLogger('posnet.tcp_server')
		self.__query_plane_messages = sets.Set()
		
	def init_query_plane(self):		
		self.__query_plane_messages.add( posnet_overlay_messages.posnet_message_type_query)
		self.__query_plane_messages.add( posnet_overlay_messages.posnet_message_type_open_link )
		self.__query_plane_messages.add( posnet_overlay_messages.posnet_message_type_ping )
		self.__query_plane_messages.add( posnet_overlay_messages.posnet_message_type_graph_request )
		self.__query_plane_messages.add( posnet_overlay_messages.posnet_message_type_draw )
	#-----------------------
	def server_activate(self):
		"""Override server_activate to set timeout on our listener socket"""
		self.socket.settimeout(posnet_tcp_server.socket_timeout)
		self.socket.setblocking(1)
		SocketServer.TCPServer.server_activate(self) 
	def stop_schedulers(self):
		""" sto pthe scedulers """
		try: self.__control_plane_scheduler.kill()
		except: pass
		try: self.__query_plane_scheduler.kill()
		except: pass

	def serve_forever(self):
		"""Override serve_forever to handle shutdown."""
		self.__running = True
		try:
			self.logger.info('posnet_tcp_server: begin serve_forever on '+str(self.__bind_address)+", PID:"+str(os.getpid()))
			while self.__running:
				self.handle_request() 	
		finally:
			self.logger.info('posnet_tcp_server: end serve_forever')
			self.stop_schedulers()
	def shutdown(self):
		self.logger.info('posnet_tcp_server: shutdown call!')
		if self.__running:
			#####self.logger.info('end serve_forever (shutdown call)')
			self.__running = 0 

	def handle_request(self):
		"""Override handle_request to trap timeout exception."""
		try:
			SocketServer.TCPServer.handle_request(self)
		except socket.timeout:
			self.logger.info('posnet_tcp_server: timeout')
			# The only reason for the timeout is so we can notice keyboard
			# interrupts on Win32, which don't interrupt accept() by default
			return 1
		except KeyboardInterrupt:
			self.shutdown() 

	def finish_request(self, request, client_address):
		""" """
		##t0 = time.clock()
		peer_addr = request.getpeername()
		##t1 = time.clock()
		#####self.logger.info("handle request from client "+str(client_address)+ ",peer_addr="+str(peer_addr) )
		buffer  = array.array("c")
		try:
			
			##t11 = time.clock()
			recv_ = request.recv(4)
			##t12 = time.clock()
			datasize  = array.array("L", recv_).pop()	
			##print "datasize",datasize
			###datasize = int(posnet_overlay_messages.unpack_bits(recv_))
			##t2 = time.clock()
			#message_type = None
			while  buffer.buffer_info()[1] * buffer.itemsize  < datasize:
				wait_size = 1024
				cbuffer_len = buffer.buffer_info()[1]*buffer.itemsize
				if wait_size > datasize - cbuffer_len: wait_size = datasize - cbuffer_len 
				data = request.recv( wait_size )
				if not data: break
				buffer.extend(data)
				###cbuffer.append(data)
				###cbuffer_len += len(data)
			###buffer = "".join(cbuffer)
			##t3 = time.clock()
			if False and buffer == posnet_tcp_server.quit_message:
				print  >>sys.stderr,"receive 'quit' message: shutdown network servers..."
				sys.stderr.flush()
				self.shutdown()
			else:
				# TODO
				answer_type = posnet_overlay_messages.array_to_overlay_message_answer_type(buffer)
				##t4 = time.clock()
				if answer_type != 0:
					#print  >>sys.stderr,"close client socket, answer type is asynchronous to",answer_type
					#sys.stderr.flush()
					request.close()
					request = (peer_addr[0],int(answer_type))  #BEWAREEEEEE!!!
				##t44 = time.clock()	
				message_type = posnet_overlay_messages.array_to_overlay_message_type( buffer )
				##print "messagetype",message_type
				##t5 = time.clock()
				def handle_posnet_message(): self.__protocol_handler(client_address,buffer,request)			
				# Query Plane / Control Plane
				if message_type  in self.__query_plane_messages:
					#####self.logger.info("request from client "+str(client_address)+ " enqueued to <query plane> scheduler")
					self.__query_plane_scheduler.enqueue(handle_posnet_message)
				else:
					#####self.logger.info("request from client "+str(client_address)+ " enqueued to <control plane> scheduler")
					self.__control_plane_scheduler.enqueue(handle_posnet_message)
				##t6 = time.clock()
				##print "TIME:",t1-t0,t11-t1,t12-t11,t2-t12,t3-t2,t4-t3,t44-t4,t5-t44,t6-t5
				##print "TIME: receive 4 bits",t12-t11
				##print "TIME: unpack_size",t2-t12
				##print "TIME: unpack_answer_type",t4-t3
				##print "TIME: close conn",t44-t4
				##print "TIME: unpack_msg",t5-t44
		except:
			raise
	#------------------------------------- 
	def close_request(self, request):
		"""Called to clean up an individual request."""
		pass #print "i'm not closed :)"	

if __name__ == "__main__":
	pass
	#from posnet_overlay_messages import unpack_overlay_message
	#myPort = int(sys.argv[1])                  
	#myaddr = ( '', myPort) 
	#def handler(add,buf,client_socket):
	#	print unpack_overlay_message( buf )
	#server = posnet_tcp_server(myaddr,handler)
	#server.serve_forever()
	
